# glscenefunctions.py
#
# GL Functions for scenes

from xmlscene_functions import *
from vector_functions import *

import OpenGL
# OpenGL.ERROR_CHECKING = False # speeds up code by disabling error checking => not good for debugging, but good for release
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.arrays import ArrayDatatype

import numpy

def createIndexedGL_TRIANGLESForPatches(patches):
    # use glBegin and glEnd
    glBegin(GL_TRIANGLES)
    for i in xrange(len(patches)):
        # index the patch in the scene by its color
        patch = patches[i]
        glColor3ub(i & 0xff, (i>>8) & 0xff, (i>>16) & 0xff)
        glVertex3f(patch.triangle.vertex[0].x, 
                   patch.triangle.vertex[0].y, 
                   patch.triangle.vertex[0].z)
        glVertex3f(patch.triangle.vertex[1].x,
                   patch.triangle.vertex[1].y,
                   patch.triangle.vertex[1].z)
        glVertex3f(patch.triangle.vertex[2].x,
                   patch.triangle.vertex[2].y,
                   patch.triangle.vertex[2].z)
    glEnd()

def createGL_TRIANGLESDisplayListForPatches(patches):
    display_list = glGenLists(1)
    glNewList(display_list, GL_COMPILE)
    createIndexedGL_TRIANGLESForPatches(patches)
    glEndList()
    return display_list

def vertexDataForPatches(patches, numpy_dtype=numpy.float32):
    vertices = []
    for i in xrange(len(patches)):
        # add each triangle vertex
        vertices += vectorListForVertex(patches[i].triangle.vertex[0])
        vertices += vectorListForVertex(patches[i].triangle.vertex[1])
        vertices += vectorListForVertex(patches[i].triangle.vertex[2])
    return numpy.array(vertices, dtype=numpy_dtype)

def colorIndicesForPatches(patches, numpy_dtype=numpy.int8):
    colors = []
    for i in xrange(len(patches)):
        # index each vertex in the scene by its color
        r = i & 0xff
        g = (i>>8) & 0xff
        b = (i>>16) & 0xff
        colors += [r, g, b, r, g, b, r, g, b]
    return numpy.array(colors, dtype=numpy_dtype)

def createGlArraysForPatches(patches):
    return [vertexDataForPatches(patches), colorIndicesForPatches(patches)]

def drawGlArraysForPatches(arrays, num_patches):
    glEnableClientState(GL_VERTEX_ARRAY)
    glEnableClientState(GL_COLOR_ARRAY)

    glVertexPointer(3, GL_FLOAT, 0, arrays[0])
    glColorPointer(3, GL_UNSIGNED_BYTE, 0, arrays[1])

    num_triangles = 3*num_patches
    glDrawArrays(GL_TRIANGLES, 0, num_triangles)

    glDisableClientState(GL_COLOR_ARRAY)
    glDisableClientState(GL_VERTEX_ARRAY)

class VBO(object):

  def __init__(self, data, usage, type=GL_ARRAY_BUFFER_ARB):
      self.buffer = glGenBuffers(1)
      self.type = type
      glBindBuffer(self.type, self.buffer)
      glBufferData(self.type, ArrayDatatype.arrayByteCount(data), ArrayDatatype.voidDataPointer(data), usage)
      glBindBuffer(self.type, 0)

  def __del__(self):
      glDeleteBuffers(1, GLuint(self.buffer))

  def bind(self):
      glBindBuffer(self.type, self.buffer)

  def unbind(self):
      glBindBuffer(self.type, 0)

  def bind_colors(self, size, type, stride=0):
      self.bind()
      glColorPointer(size, type, stride, None)

  def bind_edgeflags(self, stride=0):
      self.bind()
      glEdgeFlagPointer(stride, None)

  def bind_indexes(self, type, stride=0):
      self.bind()
      glIndexPointer(type, stride, None)

  def bind_normals(self, type, stride=0):
      self.bind()
      glNormalPointer(type, stride, None)

  def bind_texcoords(self, size, type, stride=0):
      self.bind()
      glTexCoordPointer(size, type, stride, None)

  def bind_vertexes(self, size, type, stride=0):
      self.bind()
      glVertexPointer(size, type, stride, None)

  def bind_vertexattrib(self, size, type, index, stride=0, normalized=GL_FALSE):
      self.bind()
      glVertexAttribPointer(index, size, type, normalized, stride, None)

def indicesForPatches(patches, numpy_dtype=numpy.int):
    indices = []
    for i in xrange(len(patches)):
        indices += [3*i, 3*i + 1, 3*i + 2]
    return numpy.array(indices, dtype=numpy_dtype)

def createVBOsForPatches(patches):
    # describe the vertices and vertex colors for the patch
    vertices = VBO(vertexDataForPatches(patches), GL_STATIC_DRAW)
    colors = VBO(colorIndicesForPatches(patches), GL_STATIC_DRAW)
    indices = VBO(indicesForPatches(patches), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER)

    return [vertices, colors, indices]

def drawVBOsForPatches(vbos, num_patches):
    glEnableClientState(GL_VERTEX_ARRAY)
    glEnableClientState(GL_COLOR_ARRAY)
        
    vbos[0].bind_vertexes(3, GL_FLOAT)
    vbos[1].bind_colors(3, GL_UNSIGNED_BYTE)
    
    vbos[2].bind()
    num_triangles = 3*num_patches
    glDrawElements(GL_TRIANGLES, num_triangles, GL_UNSIGNED_INT, None)

    vbos[1].unbind()
        
    glDisableClientState(GL_COLOR_ARRAY)
    glDisableClientState(GL_VERTEX_ARRAY)

def glutDisplay():
    glFlush()
    glutPostRedisplay()

def createGLUTRenderingContext(window_width, window_height):
    try:
        glutInit(sys.argv)	
    except TypeError:
        glutInit( ' '.join(sys.argv))
    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE)
    glutInitWindowPosition(0, 0)
    glutInitWindowSize(window_width, window_height)
    glutCreateWindow("Radiosity Renderer")

    glClearColor(1.0, 1.0, 1.0, 0.0)
    glClearDepth(1.0)
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LESS)

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glViewport(0, 0, window_width, window_height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(90, 1.0, 1e-3, 50)
    glMatrixMode(GL_MODELVIEW)
